/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ani.h"
#include <string>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#include "libarkbase/utils/logger.h"

[[maybe_unused]] static void GetStdString(ani_env *env, ani_string str, std::string &result)
{
    ani_size sz {};
    LOG_IF(env->String_GetUTF8Size(str, &sz) != ANI_OK, FATAL, ANI) << "Can't get UTF8 string";
    result.resize(sz + 1);

    LOG_IF(env->String_GetUTF8SubString(str, 0, sz, result.data(), result.size(), &sz) != ANI_OK, FATAL, ANI) << "Can't get UTF8 substring";
    result.resize(sz);
}

[[maybe_unused]] static bool Equal(double a1, double a2)
{
    return std::abs(a1 - a2) < 0.000001F;
}

{% set names = ["", "Direct"] -%}

{% for name in names -%}
{% if (name != "Direct" or type != "string") and (arm32 == "false" or name != "Direct") %}
void print{{ name }}{{ Type }}{{ count }}({% if name != "Direct" %}[[maybe_unused]] ani_env *env, {% endif %}
{% for k in range(count) -%}
    ani_{{ type }} a{{ loop.index }}{% if not loop.last %}, {% endif %}
{% endfor -%})
{
{% if type == "double" or type == "float" -%}
    {% for k in range(count) -%}
        LOG_IF(!Equal(a{{ loop.index }}, {{ loop.index }}.{{ loop.index }}), FATAL, ANI) << "'" << a{{ loop.index }} << "' not equal to"
                                                                                         << {{ loop.index }}.{{ loop.index }};
    {% endfor -%}
{%- elif type == "boolean" -%}
    {% for k in range(count) -%}
        LOG_IF(a{{ loop.index }} != {%- if k % 2 == 0 -%}
            true
        {%- else -%}
            false
        {%- endif -%}, FATAL, ANI) << "'" << a{{ loop.index }} << "' not equal to '"
        << {%- if k % 2 == 0 -%}
            "true"
        {%- else -%}
            "false"
        {%- endif -%} << "'";
    {% endfor -%}
{%- elif type == "string" -%}
    {% for k in range(count) -%}
        std::string s{{ loop.index }};
    {% endfor -%}
    {% for k in range(count) -%}
        GetStdString(env, a{{ loop.index }}, s{{ loop.index }});
    {% endfor -%}
    {% for k in range(count) -%}
        LOG_IF(s{{ loop.index }} != "{{ loop.index }}", FATAL, ANI) << "'" << s{{ loop.index }} << "' not equal to '"
                                                                           << {{ loop.index }} << "'";
    {% endfor -%}
{%- else %}
    {% for k in range(count) -%}
        LOG_IF(a{{ loop.index }} != {{ loop.index }}, FATAL, ANI) << "'" << a{{ loop.index }} << "' not equal to '"
                                                                           << {{ loop.index }} << "'";
    {% endfor -%}
{%- endif %}
}
{% endif %}
{% endfor -%}

const std::vector<ani_native_function> g_{{ type }}Methods = {
    {"print{{ Type }}{{ count }}", nullptr, reinterpret_cast<void *>(print{{ Type }}{{ count }})},
    {% if type != "string" and arm32 == "false" -%}
        {"printDirect{{ Type }}{{ count }}", nullptr, reinterpret_cast<void *>(printDirect{{ Type }}{{ count }})},
    {% endif -%}
    {"printQuick{{ Type }}{{ count }}", nullptr, reinterpret_cast<void *>(print{{ Type }}{{ count }})},
};

ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
{
    ani_env *env;
    LOG_IF(vm->GetEnv(ANI_VERSION_1, &env) != ANI_OK, FATAL, ANI) << "Unsupported ANI_VERSION_1";

    static const char *moduleName = "EtsAniTests";

    ani_module module;

    LOG_IF(env->FindModule(moduleName, &module) != ANI_OK, FATAL, ANI) << "Not found '" << moduleName << "'";

    for (auto &m : g_{{ type }}Methods) {
        LOG_IF(env->Module_BindNativeFunctions(module, &m, 1) != ANI_OK, FATAL, ANI) << "Can't bind function '" << m.name << "'";
    }
    *result = ANI_VERSION_1;
    return ANI_OK;
}
